Table of Contents

Module: Modules .\src\TW\API\Modules.py

Module Inheritance and Module Advice

The APIs defined here let you create modules which "subclass" other modules, by defining a module-level __bases__ attribute which lists the modules you wish to inherit from. For example:

        from TW.API import *

        import BaseModule1, BaseModule2

        __bases__ = BaseModule1, BaseModule2

        class MyClass:
            ...

        setupModule()

The setupModule() call will convert the calling module, BaseModule1, and BaseModule2 into specially altered bytecode objects and execute them (in "method-resolution order") rewriting the calling module's dictionary in the process. The result is rather like normal class inheritance, except that classes (even nested classes) are merged by name, and metaclass constraints are inherited. So a "subclassing module" need not list all the classes from its "base module" in order to change them by altering a base class in that module.

Note: All the modules listed in __bases__ must call setupModule(), even if they do not define a __bases__ or desire module inheritance themselves. This is because TransWarp cannot otherwise get access to their bytecode in a way that is compatible with the many "import hook" systems that exist for Python. (E.g. running bytecode from zip files or frozen into an executable, etc.)

Function Rebinding

All functions inherited via "module inheritance" using setupModule() (including those which are instance or class methods) have their globals rebound to point to the destination module. This means that if a function or method references a global in the module you're inheriting from, you can override that global in the "subclass module", without having to recode the function that referenced it. (This is especially useful for super() calls!)

In addition to rebinding general globals, functions which reference the global name __proceed__ are also specially rebound so that __proceed__ is the previous definition of that function, if any, in the inheritance list. (It is None if there is no previous definition.) This allows you to do the rough equivalent of a super() call (or AspectJ "around advice") without having to explicitly import the old version of a function. Note that __proceed__ is always either a function or None, so you must include self as a parameter when calling it from a method definition.

Inheritance of Metaclass Constraints

Python 2.2 enforces metaclass constraints for "new-style" classes. That is, it requires that a new class' metaclass be "compatible" with the metaclass of each of the base classes of the class, where "compatible" means "is the same as, or is a subclass of".

Python, however, does not automatically generate such a metaclass for you. You must ordinarily supply that metaclass yourself, either as an explicit __metaclass__ definition, or by having one of the base classes supply a suitable metaclass. This is fine for simple programs, where metaclasses are infrequently mixed, but more problematic for complex frameworks like TransWarp, where a variety of metaclasses are mixed and matched to supply various properties.

So, using setupModule() gives you an additional bonus: TransWarp will automatically generate the necessary metaclasses for you, so long as within any single module you don't break Python's metaclass checks. That is, if you define class A in modules M1 and M2, then as long as each definition is valid in standard Python, you can use different metaclasses for each, and TransWarp will automatically generate a new metaclass (via inheritance from the old metaclasses) if the definitions ever need to be merged. (And, of course, if you called setupModule() in both M1 and M2.)

In addition, there is an extra metaclass hook that TransWarp provides. If you define a __metaclasses__ attribute in a class definition, setupModule() will use it as a list of additional metaclasses which should be used in metaclass generation. For more information on how this and other TransWarp metaclass generation features work, please see the documentation of the TW.Utils.Meta module.

Special Considerations for Mutables and Dynamic Initialization

Both inheritance and advice are implemented by running hacked, module-level code under a "simulator" that intercepts the setting of variables. This works great for static definitions like class and def statements, constant assignments, import, etc. It also works reasonably well for many other kinds of static initialization of immutable objects

Mutable values, however, may require special considerations. For example, if a module sets up some kind of registry as a module-level variable, and an inheriting module overrides the definition, things can get tricky. If the "superclass module" writes values into that registry as part of module initialization, those values will also be written into the registry defined by the "subclass module".

Another possible issue is if the "superclass module" performs other externally visible, non-idempotent operations, such as registering classes or functions in another module's registry, printing things to the console, etc.

The simple workaround for all these considerations, however, is to move your dynamic initialization code to a module-level __init__ function.

Module-level __init__() Functions

The last thing setupModule() does before returning, is to check for a module-level __init__() function, and call it with no arguments, if it exists. This allows you to do any dynamic initialization operations (such as modifying or resetting global mutables) after inheritance has taken effect. As with any other function defined in the module, __proceed__ refers to the previous (i.e. "superclass module") definition of the function or None. This lets you can chain to your predecessors' initialization code, if needed/desired.

Note, by the way, that if you have an if __name__=="__main__" block in your module, it would probably be best if you move it inside the __init__() function, as this ensures that it will not be run repeatedly if you do not wish it to be. It will also allow other modules to inherit that code and wrap around it, if they so desire.

To-do Items

  • The adviseModule() API is as-yet untested, and setupModule() is only lightly tested so far. We need lots of test cases to make sure this thing is working right, because a lot of things are going to depend on it in future.

  • This docstring is woefully inadequate to describe all the interesting subtleties of module inheritance; a tutorial is really needed. But there does need to be a reference-style explanation as well, that describes the precise semantics of interpretation for assignments, def, and class, in modules running under simulator control.

  • Add LegacyModule("name") and loadLegacyModule("name") APIs to allow inheriting from and/or giving advice to modules which do not call setupModule().

Imported modules   
from TW.Utils.Code import *, BUILD_CLASS, STORE_NAME, MAKE_CLOSURE, MAKE_FUNCTION, LOAD_CONST, STORE_GLOBAL, CALL_FUNCTION, IMPORT_STAR, IMPORT_NAME, JUMP_ABSOLUTE, POP_TOP, ROT_FOUR, LOAD_ATTR, LOAD_GLOBAL, ROT_TWO, LOAD_LOCALS
from TW.Utils.Meta import makeClass
import sys
from sys import _getframe
from types import ModuleType
Functions   
adviseModule
getCodeListForModule
prepForSimulation
setupModule
  adviseModule 
adviseModule ( moduleName )

Exceptions   
SpecificationError( "%s is already imported and cannot be advised" % moduleName )
SpecificationError( "Advice modules cannot use '__bases__'" )
  getCodeListForModule 
getCodeListForModule ( module,  code=None )

Exceptions   
TypeError("%s is not a module in %s __bases__" %( m, name ) )
  prepForSimulation 
prepForSimulation (
        code,
        path='',
        depth=0,
        )

  setupModule 
setupModule ()

setupModule() - Build module, w/advice and inheritance

setupModule() should be called only at the very end of a module's code. This is because any code which follows setupModule() will be executed twice. (Actually, the code before setupModule() gets executed twice, also, but the module dictionary is reset in between, so its execution is cleaner.)

Classes   
Simulator

Table of Contents

This document was automatically generated on Tue Mar 05 10:33:43 2002 by HappyDoc version WORKING